package org.quincy.rock.core.lang;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.function.BiFunction;
import java.util.function.Function;

import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialClob;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.quincy.rock.core.exception.ConversionException;
import org.quincy.rock.core.exception.ParseException;
import org.quincy.rock.core.util.CoreUtil;
import org.quincy.rock.core.util.DateUtil;
import org.quincy.rock.core.util.NumberUtil;
import org.quincy.rock.core.util.StringUtil;

/**
 * <b>数据类型。</b>
 * <p><b>详细说明：</b></p>
 * <!-- 在此添加详细说明 -->
 * 无。
 * 
 * @version 1.0
 * @author quincy
 * @since 1.0
 */
@SuppressWarnings("unchecked")
public enum DataType {
	/**
	 * 布尔类型
	 */
	BOOLEAN(Types.BOOLEAN, new Class<?>[] { Boolean.class, Boolean.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return CoreUtil.string2Boolean(str);
		}

	}, "BOOL"),
	/**
	 * 字节类型
	 */
	BYTE(Types.TINYINT, new Class<?>[] { Byte.class, Byte.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return Byte.valueOf(str);
		}

	}),
	/**
	 * 短整型
	 */
	SHORT(Types.SMALLINT, new Class<?>[] { Short.class, Short.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return Short.valueOf(str);
		}

	}),
	/**
	 * 整型
	 */
	INTEGER(Types.INTEGER, new Class<?>[] { Integer.class, Integer.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return Integer.valueOf(str);
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			Number v = NumberUtil.parseNumber(str, false, f);
			return v == null ? null : v.intValue();
		}

	}, "INT"),
	/**
	 * 长整型
	 */
	LONG(Types.BIGINT, new Class<?>[] { Long.class, Long.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return Long.valueOf(str);
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			Number v = NumberUtil.parseNumber(str, false, f);
			return v == null ? null : v.longValue();
		}

	}),
	/**
	 * 超大整数
	 */
	BIGINTEGER(Types.BIGINT, new Class<?>[] { BigInteger.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return new BigInteger(str);
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			Number v = NumberUtil.parseNumber(str, false, f);
			return v == null ? null : BigInteger.valueOf(v.longValue());
		}

	}, "BIGINT"),
	/**
	 * 单精度浮点数
	 */
	FLOAT(Types.FLOAT, new Class<?>[] { Float.class, Float.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return Float.valueOf(str);
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			Number v = NumberUtil.parseNumber(str, false, f);
			return v == null ? null : v.floatValue();
		}

	}),
	/**
	 * 双精度浮点数
	 */
	DOUBLE(Types.DOUBLE, new Class<?>[] { Double.class, Double.TYPE }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return Double.valueOf(str);
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			Number v = NumberUtil.parseNumber(str, false, f);
			return v == null ? null : v.doubleValue();
		}

	}, "DECIMAL"),
	/**
	 * 超大数字
	 */
	BIGDECIMAL(Types.DECIMAL, new Class<?>[] { BigDecimal.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return new BigDecimal(str);
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			Number v = NumberUtil.parseNumber(str, false, f);
			return v == null ? null : BigDecimal.valueOf(v.doubleValue());
		}

	}),
	/**
	 * 字符串
	 */
	STRING(Types.VARCHAR, new Class<?>[] { String.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			return str;
		}

	}),
	/**
	 * 日期
	 */
	DATE(Types.DATE, new Class<?>[] { Date.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return new Date(DateUtil.parseDate(str).getTime());
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			if (StringUtil.isBlank(str))
				return null;
			try {
				long time;
				if (f instanceof FastDateFormat) {
					time = ((FastDateFormat) f).parse(str).getTime();
				} else {
					time = ((DateFormat) f).parse(str).getTime();
				}
				return new Date(time);
			} catch (Exception e) {
				throw new ParseException(e.getMessage(), e);
			}
		}

	}),
	/**
	 * 时间
	 */
	TIME(Types.TIME, new Class<?>[] { Time.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return new Time(DateUtil.parseTime(str).getTime());
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			if (StringUtil.isBlank(str))
				return null;
			try {
				long time;
				if (f instanceof FastDateFormat) {
					time = ((FastDateFormat) f).parse(str).getTime();
				} else {
					time = ((DateFormat) f).parse(str).getTime();
				}
				return new Time(time);
			} catch (Exception e) {
				throw new ParseException(e.getMessage(), e);
			}
		}

	}),
	/**
	 * 时间戳
	 */
	DATETIME(Types.TIMESTAMP, new Class<?>[] { Timestamp.class, java.util.Date.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (StringUtil.isBlank(str))
				return null;
			return new Timestamp(DateUtil.parseDateTime(str).getTime());
		}

	}, new BiFunction<String, Format, Object>() {

		@Override
		public Object apply(String str, Format f) {
			if (StringUtil.isBlank(str))
				return null;
			try {
				long time;
				if (f instanceof FastDateFormat) {
					time = ((FastDateFormat) f).parse(str).getTime();
				} else {
					time = ((DateFormat) f).parse(str).getTime();
				}
				return new Timestamp(time);
			} catch (Exception e) {
				throw new ParseException(e.getMessage(), e);
			}
		}

	}, "TIMESTAMP"),
	/**
	 * 二进制类型
	 */
	BINARY(Types.BINARY, new Class<?>[] { ArrayUtils.EMPTY_BYTE_ARRAY.getClass() }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			if (str == null)
				return null;
			else if (str.length() == 0 || str.trim().length() == 0)
				return ArrayUtils.EMPTY_BYTE_ARRAY;
			else
				return CoreUtil.hexString2ByteArray(str);
		}

	}, "BIN"),
	/**
	 * BLOB类型
	 */
	BLOB(Types.BLOB, new Class<?>[] { Blob.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			try {
				if (str == null)
					return null;
				else if (str.length() == 0 || str.trim().length() == 0)
					return new SerialBlob(ArrayUtils.EMPTY_BYTE_ARRAY);
				else
					return new SerialBlob(CoreUtil.hexString2ByteArray(str));
			} catch (Exception e) {
				throw new ConversionException(e.getMessage(), e);
			}
		}

	}),
	/**
	 * CLOB类型
	 */
	CLOB(Types.CLOB, new Class<?>[] { Clob.class }, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			try {
				if (str == null)
					return null;
				else
					return new SerialClob(str.toCharArray());
			} catch (Exception e) {
				throw new ConversionException(e.getMessage(), e);
			}
		}

	}),
	/**
	 * 未知类型
	 */
	UNKNOW(Integer.MIN_VALUE, new Class<?>[] {}, new Function<String, Object>() {

		@Override
		public Object apply(String str) {
			return str;
		}

	});

	/**
	 * sqlType。
	 */
	private int sqlType;
	/**
	 * types。
	 */
	private Class<?>[] types;
	/**
	 * 别名。
	 */
	private String[] alias;

	private Function<String, Object> parser;

	private BiFunction<String, Format, Object> parserByFmt;

	/**
	 * <b>构造方法。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param sqlType sql类型
	 * @param types java类型
	 * @param parser 解析函数
	 * @param alias 别名
	 */
	DataType(int sqlType, Class<?>[] types, Function<String, Object> parser, String... alias) {
		this.sqlType = sqlType;
		this.types = types;
		this.parser = parser;
		this.alias = alias;
	}

	/**
	 * <b>构造方法。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param sqlType sql类型
	 * @param types java类型
	 * @param parser 解析函数
	 * @param parserByFmt 带格式串的解析函数
	 * @param alias 别名
	 */
	DataType(int sqlType, Class<?>[] types, Function<String, Object> parser,
			BiFunction<String, Format, Object> parserByFmt, String... alias) {
		this(sqlType, types, parser, alias);
		this.parserByFmt = parserByFmt;
	}

	/**
	 * <b>获得Format。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 返回的Format不是线程安全的。
	 * @param fmt 格式串
	 * @return Format
	 */
	public Format getFormat(String fmt) {
		Format format;
		switch (this) {
		case DATE:
		case TIME:
		case DATETIME:
			format = new SimpleDateFormat(fmt);
			break;
		default:
			format = new DecimalFormat(fmt);
			break;
		}
		return format;
	}

	/**
	 * <b>解析字符串。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param str 字符串值
	 * @return 解析后的值
	 */
	public <T> T parse(String str) {
		if (str == null)
			return null;
		return (T) parser.apply(str);
	}

	/**
	 * <b>解析字符串。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param str 字符串值
	 * @param fmt 格式串
	 * @param tryAll 尽最大可能进行解析
	 * @return 解析后的值
	 */
	public <T> T parse(String str, String fmt, boolean tryAll) {
		if (str == null)
			return null;
		if (StringUtil.isEmpty(fmt) || parserByFmt == null)
			return parse(str);
		//
		Format format = getFormat(fmt);
		try {
			return (T) parserByFmt.apply(str, format);
		} catch (Exception e) {
			if (tryAll)
				return parse(str);
			else if (e instanceof RuntimeException)
				throw (RuntimeException) e;
			else
				throw new ParseException(e.getMessage(), e);
		}
	}

	/**
	 * <b>解析字符串。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param str 字符串值
	 * @param fmt Format
	 * @param tryAll 尽最大可能进行解析
	 * @return 解析后的值
	 */
	public <T> T parse(String str, Format fmt, boolean tryAll) {
		if (str == null)
			return null;
		if (fmt == null || parserByFmt == null)
			return parse(str);
		//
		try {
			return (T) parserByFmt.apply(str, fmt);
		} catch (Exception e) {
			if (tryAll)
				return parse(str);
			else if (e instanceof RuntimeException)
				throw (RuntimeException) e;
			else
				throw new ParseException(e.getMessage(), e);
		}
	}

	/**
	 * <b>解析字符串。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param str 字符串值
	 * @param fmts 格式串
	 * @return 解析后的值
	 */
	public <T> T parses(String str, String... fmts) {
		if (str == null)
			return null;
		if (ArrayUtils.isEmpty(fmts) || parserByFmt == null)
			return parse(str);
		for (String fmt : fmts) {
			Format format = getFormat(fmt);
			try {
				return (T) parserByFmt.apply(str, format);
			} catch (Exception e) {
			}
		}
		return parse(str);
	}

	/**
	 * <b>解析字符串。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param str 字符串值
	 * @param fmts Format
	 * @return 解析后的值
	 */
	public <T> T parses(String str, Format... fmts) {
		if (str == null)
			return null;
		if (ArrayUtils.isEmpty(fmts) || parserByFmt == null)
			return parse(str);
		for (Format fmt : fmts) {
			try {
				return (T) parserByFmt.apply(str, fmt);
			} catch (Exception e) {
			}
		}
		return parse(str);
	}

	/**
	 * <b>是否是名称或别名。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param uName 名称或别名
	 * @return 是否是名称或别名
	 */
	public boolean isNameOrAlias(String uName) {
		if (uName.equals(this.name())) {
			return true;
		} else if (alias != null && alias.length > 0) {
			for (String alia : alias) {
				if (uName.equals(alia)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * <b>是否是兼容的java类型。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param type java类型
	 * @return 是否是兼容的java类型
	 */
	public boolean isType(Class<?> type) {
		if (type != null) {
			for (Class<?> clazz : types) {
				if (clazz == type || clazz.isAssignableFrom(type))
					return true;
			}
		}
		return false;
	}

	/**
	 * <b>获得对应的sql类型。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @return sql类型
	 */
	public int sqlType() {
		return sqlType;
	}

	/**
	 * <b>返回枚举数据类型</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param name 名称或别名
	 * @return DataType
	 */
	public static DataType of(String name) {
		if (name == null)
			throw new NullPointerException("Name is null");
		String uName = name.toUpperCase();
		for (DataType dt : values()) {
			if (dt.isNameOrAlias(uName))
				return dt;
		}
		throw new IllegalArgumentException("No enum constant DataType." + name);
	}

	/**
	 * <b>返回枚举数据类型。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param type java类型
	 * @return DataType
	 */
	public static DataType of(Class<?> type) {
		if (type != null) {
			for (DataType dt : values()) {
				if (dt.isType(type)) {
					return dt;
				}
			}
		}
		return DataType.UNKNOW;
	}

	/**
	 * <b>返回value的数据类型。</b>
	 * <p><b>详细说明：</b></p>
	 * <!-- 在此添加详细说明 -->
	 * 无。
	 * @param value 对象值
	 * @return value的数据类型
	 */
	public static DataType of(Object value) {
		return value == null ? DataType.UNKNOW : of(value.getClass());
	}

	public static DataType of(int sqlType, int precision, int scale) {
		DataType dataType = DataType.UNKNOW;
		switch (sqlType) {
		case Types.BOOLEAN:
		case Types.BIT:
			dataType = DataType.BOOLEAN;
			break;
		case Types.TINYINT:
			dataType = DataType.BYTE;
			break;
		case Types.SMALLINT:
			dataType = DataType.SHORT;
			break;
		case Types.INTEGER:
			dataType = DataType.INTEGER;
			break;
		case Types.BIGINT:
			dataType = DataType.LONG;
			break;
		case Types.FLOAT:
		case Types.REAL:
			dataType = DataType.FLOAT;
			break;
		case Types.DOUBLE:
			dataType = DataType.DOUBLE;
			break;
		case Types.DECIMAL:
		case Types.NUMERIC:
			dataType = scale > 0 ? DataType.DOUBLE : precision >= 10 ? DataType.LONG : DataType.INTEGER;
			break;
		case Types.CHAR:
		case Types.VARCHAR:
		case Types.NCHAR:
		case Types.NVARCHAR:
		case Types.LONGVARCHAR:
		case Types.LONGNVARCHAR:
		case Types.ROWID:
			dataType = DataType.STRING;
			break;
		case Types.DATE:
			dataType = DataType.DATE;
			break;
		case Types.TIME:
		case Types.TIME_WITH_TIMEZONE:
			dataType = DataType.TIME;
			break;
		case Types.TIMESTAMP:
		case Types.TIMESTAMP_WITH_TIMEZONE:
			dataType = DataType.DATETIME;
			break;
		case Types.BINARY:
		case Types.ARRAY:
		case Types.VARBINARY:
			dataType = DataType.BINARY;
			break;
		case Types.CLOB:
		case Types.NCLOB:
			dataType = DataType.CLOB;
			break;
		case Types.BLOB:
		case Types.LONGVARBINARY:
			dataType = DataType.BLOB;
			break;
		default:
			break;
		}
		return dataType;
	}
}
